package cn.com.chengmandian.bike.service.impl;

import cn.com.chengmandian.bike.constants.BikeConstants;
import cn.com.chengmandian.bike.constants.ScanRecordTypeEnum;
import cn.com.chengmandian.bike.dto.MemberLocationForm;
import cn.com.chengmandian.bike.model.*;
import cn.com.chengmandian.bike.mapper.EbDeviceBikeMapper;
import cn.com.chengmandian.bike.model.vo.DeviceBikeVo;
import cn.com.chengmandian.bike.service.*;
import cn.com.chengmandian.core.common.constants.CommonConstant;
import cn.com.chengmandian.core.common.utils.LnglatUtil;
import cn.com.chengmandian.core.redis.cache.AgentParamCacheService;
import cn.com.chengmandian.core.redis.service.RedisService;
import cn.com.chengmandian.core.web.exceptions.BadResponseException;
import cn.com.chengmandian.core.web.pojo.*;
import cn.com.chengmandian.core.web.utils.LocalMapUtil;
import cn.com.chengmandian.service.bike.IotBikeClient;
import cn.com.chengmandian.service.bike.constants.*;
import cn.com.chengmandian.service.bike.form.SendBikeCommandForm;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Date;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 电单车运营-车辆信息表 服务实现类
 * </p>
 *
 * @author AutoGenerator
 * @since 2021-12-08
 */
@Slf4j
@Service
public class EbDeviceBikeServiceImpl extends ServiceImpl<EbDeviceBikeMapper, EbDeviceBike> implements IEbDeviceBikeService {

    @Autowired
    private EbDeviceBikeMapper deviceBikeMapper;

    @Autowired
    private IEbRideOrderService rideOrderService;

    @Autowired
    private IEbPriceRuleService priceRuleService;

    @Autowired
    private AgentParamCacheService agentParamCacheService;

    @Autowired
    private IotBikeClient iotBikeClient;

    @Autowired
    private IEbOperationStationService operationStationService;

    @Autowired
    private IEbRideHelmetService rideHelmetService;

    @Autowired
    private IEbScanRecordService scanRecordService;

    @Autowired
    private RedisService redisService;

    @Override
    public void checkOpenBike(AgentUser agentUser, EbDeviceBike deviceBike, BigDecimal lng, BigDecimal lat) {
        BikeAreaParam bikeParam = agentParamCacheService.getParam(LocalMapUtil.getAgentInfo().getId(), deviceBike.getAreaId());
        if (StrUtil.isNotBlank(deviceBike.getSysTagsSearch())) {
            if (deviceBike.getSysTagsSearch().indexOf(BikeSysTagEnum.OFFLINE.getCode()) != -1) {
                throw new BadResponseException(512, "车辆暂时无法骑行，请换一辆车");
            }

            if (deviceBike.getSysTagsSearch().indexOf(BikeSysTagEnum.OUT_CONTACT.getCode()) != -1) {
                throw new BadResponseException(512, "车辆暂时无法骑行，请换一辆车");
            }
        }
        if (deviceBike.getLng() == null || deviceBike.getLat() == null) {
            throw new BadResponseException(512, "车辆没有定位信息");
        }
        // 因闲置原因，需要调度
        if (deviceBike.getStatus().equals(BikeStatusEnum.WAIT_DISPATCH_STATUS.getCode()) && deviceBike.getStatusReason() != null && deviceBike.getStatusReason().equals(BikeStatusReasonEnum.IDLE_TO_DISPATCH.getCode())) {
            // 允许骑行，但不改车辆状态，等车辆下单后再改
        } else if (!deviceBike.getStatus().equals(BikeStatusEnum.USE_STATUS.getCode())) {
            // 细分扫码未投放原因
            this.scanNoPutReason(deviceBike, agentUser, lng, lat);
            throw new BadResponseException(508, "车辆当前未投放使用");
        }
        if (!deviceBike.getUseStatus().equals(DeviceBikeUseStatusEnum.IDLE.getCode())) {
            throw new BadResponseException(509, "车辆已在使用中");
        }
        if (deviceBike.getSoc() <= bikeParam.getUserBikeRideSoc()) {
            throw new BadResponseException(510, "车辆电量过低，请换一台使用");
        }
        if (!deviceBike.getIsInArea().equals(CommonConstant.ONE)) {
            throw new BadResponseException(511, "车辆当前不在运营区");
        }
        if (bikeParam.getUserOpenLockDistance() != null && bikeParam.getUserOpenLockDistance() > 0) {
            if (LnglatUtil.getDistance(lng.doubleValue(),
                    lat.doubleValue(),
                    deviceBike.getLng().doubleValue(),
                    deviceBike.getLat().doubleValue()) >= bikeParam.getUserOpenLockDistance()) {
//                log.error("车辆：{},用户坐标：{},{}，车辆坐标：{},{}", deviceBike.getBikeNo(), lng.doubleValue(), lat.doubleValue(), deviceBike.getLng().doubleValue(), deviceBike.getLat().doubleValue());
                throw new BadResponseException(513, "您的位置必须位于车辆" + bikeParam.getUserOpenLockDistance() + "米范围内");
            }
        }

        // 检查车辆站点是否被删除（存在站点删除 车辆定位未被更新）
        if(deviceBike.getStationId() != null) {
            EbOperationStation operationStation = operationStationService.getById(deviceBike.getStationId());
            if(operationStation == null) {
                throw new BadResponseException("未获取到车辆站点信息，请您换车扫码骑行！");
            }
        }
    }

    // 细分扫码车辆未投放原因
    private void scanNoPutReason(EbDeviceBike deviceBike, AgentUser agentUser, BigDecimal memberLng, BigDecimal memberLat) {
        try {
            ScanRecordTypeEnum recordTypeEnum = null;
            // 车辆状态生产中
            if (deviceBike.getStatus().equals(BikeStatusEnum.PRODUCTION_STATUS.getCode())) {
                recordTypeEnum = ScanRecordTypeEnum.PRODUCTION;

                // 车辆状态未投放
            } else if (deviceBike.getStatus().equals(BikeStatusEnum.NO_STATUS.getCode())) {
                recordTypeEnum = ScanRecordTypeEnum.NOT_RELEASED;

                // 车辆状态暂停中
            } else if (deviceBike.getStatus().equals(BikeStatusEnum.STOP_STATUS.getCode())) {
                recordTypeEnum = ScanRecordTypeEnum.STOP;

                // 车辆状态待拖回
            } else if (deviceBike.getStatus().equals(BikeStatusEnum.BACK_STATUS.getCode())) {
                recordTypeEnum = ScanRecordTypeEnum.BACK;

                // 车辆状态维修中
            } else if (deviceBike.getStatus().equals(BikeStatusEnum.REPAIR_STATUS.getCode())) {
                recordTypeEnum = ScanRecordTypeEnum.REPAIR;

                // 车辆状态待调度
            } else if (deviceBike.getStatus().equals(BikeStatusEnum.WAIT_DISPATCH_STATUS.getCode())) {
                recordTypeEnum = ScanRecordTypeEnum.WAIT_DISPATCH;

                // 车辆状态调度中
            } else if (deviceBike.getStatus().equals(BikeStatusEnum.DISPATCH_STATUS.getCode())) {
                recordTypeEnum = ScanRecordTypeEnum.DISPATCH;

                // 车辆状态已报废
            } else if (deviceBike.getStatus().equals(BikeStatusEnum.SCRAP_STATUS.getCode())) {
                recordTypeEnum = ScanRecordTypeEnum.SCRAP;

                // 车辆状态已丢失
            } else if (deviceBike.getStatus().equals(BikeStatusEnum.LOSE_STATUS.getCode())) {
                recordTypeEnum = ScanRecordTypeEnum.LOSE;

                // 车辆状态待维修
            } else if (deviceBike.getStatus().equals(BikeStatusEnum.WAIT_REPAIR_STATUS.getCode())) {
                recordTypeEnum = ScanRecordTypeEnum.WAIT_REPAIR;
            }

            // 新增扫码失败记录
            if (recordTypeEnum != null && agentUser != null) {
                scanRecordService.generateRecord(recordTypeEnum, null, agentUser, deviceBike, memberLng, memberLat);
            }
        }catch (Exception ex) {
            log.error("细分未投放扫码失败新增扫码记录失败:{}", ex.getMessage());
        }
    }

    /**
     * 检查是否允许停车
     */
    @Override
    public void checkCloseBike(EbDeviceBike deviceBike, EbRideOrder rideOrder, Integer isDispatch, Integer isPayHelmetMoney, Integer userId, String userName) {
        BikeAreaParam bikeAreaParam = agentParamCacheService.getParam(deviceBike.getAgentId(), deviceBike.getAreaId());
        // 用户暂未接受调度费用
        EbPriceRule priceRule = priceRuleService.getRule(deviceBike.getAreaId());

        // 禁停区检测
        if (deviceBike.getNoparkingId() != null) {
            // 发送还车失败语音
            this.noInStopAreaSendVoice(deviceBike.getAgentId(), deviceBike.getBikeNo(), "禁停区还车提醒");

            throw new BadResponseException(503, "当前处于禁停区，请到P点停车");
        }

        // 头盔检测
        if(!rideHelmetService.checkReturnBikeHelmet(bikeAreaParam, deviceBike, rideOrder)) {
            if (isPayHelmetMoney == null || isPayHelmetMoney.equals(CommonConstant.ZERO)) {
                this.sendHelloVoice(deviceBike.getAgentId(), deviceBike.getBikeNo(), "还车检查");

                if (priceRule.getIsNoHelmet() != null && priceRule.getIsNoHelmet().equals(CommonConstant.ONE)) {
                    JSONObject data = new JSONObject();
                    data.put("fee", priceRule.getHelmetPay());
                    throw new BadResponseException(517, "头盔未关锁或头盔摆放位置不正确" + priceRule.getHelmetPay() + "元", data);
                }
                throw new BadResponseException(516, "头盔未关锁或头盔摆放位置不正确");
            }
        }

        // 服务区检测
        if (deviceBike.getIsInArea().equals(CommonConstant.ZERO)) {
            // 发送还车失败语音
            if (isDispatch == null || CommonConstant.ZERO.equals(isDispatch)) {
                this.noInStopAreaSendVoice(deviceBike.getAgentId(), deviceBike.getBikeNo(), "服务区还车提醒");
            }

            // 系统允许超区
            if (priceRule.getIsOpenAreaDispatch().equals(CommonConstant.ONE)) {
                // 有调度费
                if (priceRule.getSuperDispatchFee().compareTo(new BigDecimal(0)) > 0) {
                    if (isDispatch == null || isDispatch.equals(CommonConstant.ZERO)) {
                        JSONObject data = new JSONObject();
                        data.put("fee", priceRule.getSuperDispatchFee());
                        throw new BadResponseException(511, "当前不在服务区，停车需支付" + priceRule.getSuperDispatchFee() + "元调度费", data);
                    }
                }
            } else {
                throw new BadResponseException(501, "当前不在服务区，请到P点停车");
            }
        }
        // 站点检测
        if (deviceBike.getStationId() == null) {
            // 尚未同意调度费 发送还车失败语音 防止与锁车指令冲突
            if (isDispatch == null || CommonConstant.ZERO.equals(isDispatch)) {
                this.noInStopAreaSendVoice(deviceBike.getAgentId(), deviceBike.getBikeNo(), "站外还车提醒");
            }

            // 系统允许站外
            if (priceRule.getIsOpenDispatch().equals(CommonConstant.ONE)) {
                // 有调度费
                if (priceRule.getStationDispatchFee().compareTo(new BigDecimal(0)) > 0) {
                    if (isDispatch == null || isDispatch.equals(CommonConstant.ZERO)) {
                        BigDecimal stationDispatchFee = priceRule.getStationDispatchFee();
                        // 获取当前车辆最新订单
                        EbRideOrder ebRideOrder = rideOrderService.getOne(new LambdaQueryWrapper<EbRideOrder>()
                                .eq(EbRideOrder::getBikeId, deviceBike.getId())
                                .orderByDesc(EbRideOrder::getId)
                                .last("limit 0,1"));
                        if (ebRideOrder != null) {
                            Long diffMinute = DateUtil.between(ebRideOrder.getOpenTime(), DateUtil.date(), DateUnit.SECOND);
                            if ((diffMinute <= priceRule.getFreeMinuteRiding() * 60) && priceRule.getIsRidingDispatch().equals(CommonConstant.ZERO)) {
                                stationDispatchFee = BigDecimal.ZERO;
                            }
                        }
                        JSONObject data = new JSONObject();
                        data.put("fee", stationDispatchFee);
                        throw new BadResponseException(512, "当前不在停车点，停车需支付" + stationDispatchFee + "元调度费", data);
                    }
                }
            } else {
                throw new BadResponseException(502, "当前不在停车点，请到P点停车");
            }
        }

        // rfid停车检查
        if(deviceBike.getStationId() != null) {
            EbOperationStation station = operationStationService.getById(deviceBike.getStationId());
            if (station.getRfidStopEnabled() != null && station.getRfidStopEnabled() >= CommonConstant.ZERO) {
                if (bikeAreaParam != null && station.getRfidStopEnabled().equals(CommonConstant.ZERO)) {
                    bikeAreaParam.setUserStopBikeRfidEnabled(false);
                } else if (bikeAreaParam != null && station.getRfidStopEnabled().equals(CommonConstant.ONE)) {
                    bikeAreaParam.setUserStopBikeRfidEnabled(true);
                }
            }
        }
        if(bikeAreaParam != null && bikeAreaParam.getUserStopBikeRfidEnabled()) {
            if(StrUtil.isBlank(deviceBike.getRfidNo())) {
                // 尚未同意调度费 发送还车失败语音 防止与锁车指令冲突
                if (isDispatch == null || CommonConstant.ZERO.equals(isDispatch)) {
                    this.noInStopAreaSendVoice(deviceBike.getAgentId(), deviceBike.getBikeNo(), "RFID还车失败提醒");
                }

                // 不符合停车规范时 根据配置是否有站外调度费 来提醒用户
                if(priceRule.getIsOpenDispatch().equals(CommonConstant.ONE)) {
                    if (priceRule.getStationDispatchFee().compareTo(new BigDecimal(0)) > 0) {
                        if (isDispatch == null || isDispatch.equals(CommonConstant.ZERO)) {
                            BigDecimal stationDispatchFee = priceRule.getStationDispatchFee();
                            // 获取当前车辆最新订单
                            EbRideOrder ebRideOrder = rideOrderService.getOne(new LambdaQueryWrapper<EbRideOrder>()
                                    .eq(EbRideOrder::getBikeId, deviceBike.getId())
                                    .orderByDesc(EbRideOrder::getId)
                                    .last("limit 0,1"));
                            if (ebRideOrder != null) {
                                Long diffMinute = DateUtil.between(ebRideOrder.getOpenTime(), DateUtil.date(), DateUnit.SECOND);
                                if ((diffMinute <= priceRule.getFreeMinuteRiding() * 60) && priceRule.getIsRidingDispatch().equals(CommonConstant.ZERO)) {
                                    stationDispatchFee = BigDecimal.ZERO;
                                }
                            }
                            JSONObject data = new JSONObject();
                            data.put("fee", stationDispatchFee);
                            throw new BadResponseException(522, "当前不在停车点，停车需支付" + stationDispatchFee + "元调度费", data);
                        }
                    }
                }else {
                    throw new BadResponseException(502, "当前不在停车点，请到P点停车");
                }
            }
        }
    }

    @Override
    public void updateScanInfo(EbDeviceBike ebDeviceBike, AgentUser agentUser, MemberLocationForm memberLocationForm) {
        ebDeviceBike.setLastScanId(agentUser.getId());
        ebDeviceBike.setLastScanName(agentUser.getName());
        ebDeviceBike.setLastScanPerson(CommonConstant.ONE);
        ebDeviceBike.setLastScanLng(memberLocationForm.getLng());
        ebDeviceBike.setLastScanLat(memberLocationForm.getLat());
        ebDeviceBike.setLastScanTime(DateUtil.date());
        this.updateById(ebDeviceBike);
    }

    @Override
    public void noInStopAreaSendVoice(Integer agentId, String bikeNo, String sysName) {
        try {
            SendBikeCommandForm commandForm = new SendBikeCommandForm()
                    .setCommand(DeviceCommandEnum.RETURN_FAIL_VOICE.getType())
                    .setAgentId(agentId)
                    .setBikeNo(bikeNo)
                    .setUserId(0)
                    .setUserName(sysName)
                    .setUserType(UserTypeEnum.SYS.getCode());
            iotBikeClient.sendBikeCommand(commandForm);
        } catch (Exception ex) {
            log.error("发送语音指令:{}", ex.getMessage());
        }
    }

    // 发送归返头盔语音
    private void sendHelloVoice(Integer agentId, String bikeNo, String sysName) {
        try {
            // 发送归返头盔语音
            SendBikeCommandForm commandForm = new SendBikeCommandForm()
                .setCommand(DeviceCommandEnum.HELMET_LOCK_RETURN_VOICE.getType())
                .setAgentId(agentId)
                .setBikeNo(bikeNo)
                .setUserId(0)
                .setUserName(sysName)
                .setUserType(UserTypeEnum.SYS.getCode());
            iotBikeClient.sendBikeCommand(commandForm);
        }catch (Exception ex) {
            log.error("发送语音指令:{}", ex.getMessage());
        }
    }

    /**
     * 获取附近车辆
     */
    @Override
    public List<DeviceBikeVo> getNearList(Integer agentId, Integer areaId, MemberLocationForm memberLocationForm) {
        BikeAreaParam bikeAreaParam = agentParamCacheService.getParam(agentId, areaId);
        BigDecimal nearLng = memberLocationForm.getLng();
        BigDecimal nearLat = memberLocationForm.getLat();
        // 定位方式 用户定位
        if (bikeAreaParam.getUserNearBikeLocationType() != null && bikeAreaParam.getUserNearBikeLocationType().equals(CommonConstant.TWO)) {
            if(memberLocationForm.getMemberLng() != null && memberLocationForm.getMemberLat() != null) {
                nearLng = memberLocationForm.getMemberLng();
                nearLat = memberLocationForm.getMemberLat();
            }
        }
        // 默认为1000米
        BigDecimal nearBikeRang = BikeConstants.NEAR_BIKE_RANG;
        BigDecimal nearBikeRangMeter = BigDecimal.valueOf(0.00001);
        // 距离范围
        if(bikeAreaParam.getUserSeeBikeNearDistance() != null){
            if(bikeAreaParam.getUserSeeBikeNearDistance() <= 0){
                return new ArrayList<DeviceBikeVo>();
            }
            nearBikeRang = nearBikeRangMeter.multiply(BigDecimal.valueOf(bikeAreaParam.getUserSeeBikeNearDistance()));
        }
        return deviceBikeMapper.getNearBikeList(areaId,
                bikeAreaParam.getUserBikeRideSoc(),
                nearLat,
                nearLng,
                nearLat.subtract(nearBikeRang).setScale(6, RoundingMode.HALF_UP),
                nearLat.add(nearBikeRang).setScale(6, RoundingMode.HALF_UP),
                nearLng.subtract(nearBikeRang).setScale(6, RoundingMode.HALF_UP),
                nearLng.add(nearBikeRang).setScale(6, RoundingMode.HALF_UP));
    }

    /**
     * 获取车辆信息
     */
    @Override
    public DeviceBikeVo getBike(String bikeNo) {
        DeviceBikeVo deviceBikeVo = deviceBikeMapper.getBike(bikeNo);
        Assert.notNull(deviceBikeVo, "车辆编号不存在");
        return deviceBikeVo;
    }

    /**
     * 根据编号查找车辆
     */
    @Override
    public EbDeviceBike getByNo(String bikeNo) {
        EbDeviceBike deviceBike = this.getOne(new LambdaQueryWrapper<EbDeviceBike>().eq(EbDeviceBike::getBikeNo, bikeNo).last("limit 1"));
        Assert.notNull(deviceBike, "车辆编号不存在");
        return deviceBike;
    }

}
